﻿#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<stdio.h>
#include "res.h" 

string token = "";        //用于保存当前识别结果的字符串
int cur_length = 0; //当前识别的运算符长度
int state = 0;      //当前状态，初始状态为0
int lines = 1;       //当前识别的行数
unsigned long long chars = 0;//字符个数
int each_type_nums[7] = { 0 }; //分别记录记录标识符，常数，运算符，界符，关键字，字符串数量
string data_type[8] = { "标识符", "常数", "运算符", "界符", "关键字", "字符串", "字符" };
string error_type[4] = { "非法输入", "", "未结束识别的记号" };

void read_program(FILE* in);     //读取程序源文件并且进行词法分析
char get_next(FILE* in); //获取一个字符
void output(string data, int dt_type); //打印词法分析结果
void warning(string data, int er_type); //打印错误信息
void print_counter(); //打印字符个数等

int main()
{
	FILE* in = fopen("test.txt", "r");
	if (!in) {
		cout << "文件读取错误" << endl;
		exit(1);
	}
	init();
	read_program(in);
	if (token != "")
		warning(token, 2);
	print_counter();
	fclose(in);
	return 0;
}

void read_program(FILE* in)
{
	char current = get_next(in);
	int type = character[current]; //获取字符类型
	while (current != '\0')        //等于'\0'时说明源文件读取结束
	{
		type = character[current];
		if (current == '\n')
			lines++;
		switch (state) //根据当前状态做转移
		{
		case 0: //正在初始状态
			switch (type)
			{
			case 0:
			case 4: //空白符，回车符，制表符，换行符，或界符
				state = 0;
				if (current == '\"')
				{
					token += "\"";
					state = 10; //开始识别字符串
					break;
				}
				else if (current == '\'')
				{
					token += "\'";
					state = 11;
					break;
				}
				else if (type == 4)
				{
					string temp = "";
					temp += current;
					output(temp, 3);
				}
				current = get_next(in);
				break;
			case 1: //类型：字母或下划线，进入标识符或关键字识别模式
				token += current;
				state = 1;
				current = get_next(in);
				break;
			case 2: //类型：数字，进入常数识别模式
				token += current;
				state = 2;
				current = get_next(in);
				break;
			case 3: //类型：运算符，进入运算符识别模式
				token += current;
				state = 8;
				current = get_next(in);
				break;
			default: //发生错误，尝试恢复
					 //错误原因：识别到了非字符集的输入，采取动作：忽略
				state = 0;
				string temp = "";
				temp += current;
				warning(temp, 0);
				current = ' ';
				break;
			}
			break;
		case 1: //正在尝试识别标识符或关键字
			switch (type)
			{
			case 1:
			case 2: //标识符识别模式下可以添加任意多个数字或下划线
				token += current;
				state = 1;
				current = get_next(in);
				break;
			case 0:
			case 3:
			case 4: //类型：界符，标识符识别结束
				if (reseve_words.find(token) != reseve_words.end())
					output(token, 4);
				else
					output(token, 0);
				token = "";
				state = 0;
				break;
			default: //发生错误
					 //错误原因：识别标识符时出现了非法字符
					 //恢复方法：将此标志视为空白符，跳过
				state = 1;
				string temp = "";
				temp += current;
				warning(temp, 0);
				current = get_next(in);
				break;
			}
			break;
		case 2:                 //正在尝试识别常数
			if (current == '.') //转到小数处理状态
			{
				token += current;
				state = 3;
				current = get_next(in);
			}
			else if (current == 'e') //转到指数处理状态
			{
				token += current;
				state = 5;
				current = get_next(in);
			}
			else if (type == 2) //仍为常数处理状态
			{
				token += current;
				state = 2;
				current = get_next(in);
			}
			else if (type == 4 || type == 3) //界符或运算符
			{
				output(token, 1);
				token = "";
				state = 0;
			}
			else
				//错误原因：在识别常数时出现了意外的字符（字母，下划线，其他字符等）
				//恢复方法：当做界符处理
			{
				output(token, 1);
				token = "";
				string temp = "";
				temp += current;
				warning(temp, 0);
				state = 0;
				current = get_next(in);
			}
			break;
		case 3:               //正在尝试识别小数点后的数字
			if (type == 2) //捕获到了一个数字
			{
				token += current;
				state = 4;
				current = get_next(in);
			}
			else
				//错误原因：没有读取到数字
				//恢复手段：默认小数以0结尾，并继续小数识别
			{
				token += '0';
				state = 4;
				string temp = "";
				temp += current;
				warning(temp, 0);
				current = get_next(in);
			}
			break;
		case 4:                 //正在尝试识别小数点后第一个数字之后的数字
			if (current == 'e') //转到指数处理状态
			{
				token += current;
				state = 5;
				current = get_next(in);
			}
			else if (type == 2) //仍为常数处理状态
			{
				token += current;
				state = 4;
				current = get_next(in);
			}
			else if (type == 4 || type == 3) //界符
			{
				output(token, 1);
				token = "";
				state = 0;
			}
			else
				//视为界符
			{
				output(token, 1);
				token = "";
				string temp = "";
				temp += current;
				warning(temp, 0);
				state = 0;
				current = get_next(in);
			}
			break;
		case 5:                                   //正在尝试识别指数
			if (current == '+' || current == '-') //转到指数处理状态
			{
				token += current;
				state = 6;
				current = get_next(in);
			}
			else if (type == 2) //仍为常数处理状态
			{
				token += current;
				state = 7;
				current = get_next(in);
			}
			else
				//错误原因：未识别到有效的正负号和常数
				//恢复办法：默认视为识别到了"+"
			{
				token += '+';
				string temp = "";
				temp += current;
				warning(temp, 0);
				state = 6;
				current = get_next(in);
			}
			break;
		case 6:               //正在尝试识别指数标志e之后的部分（已经遇到了有效数字，可以结束）
			if (type == 2) //仍为常数处理状态
			{
				token += current;
				state = 6;
				current = get_next(in);
			}
			else if (type == 4 || type == 3) //界符
			{
				output(token, 1);
				token = "";
				state = 0;
			}
			else
				//错误原因：读取到了非常数的字符
				//恢复方法：视为界符
			{
				output(token, 1);
				token = "";
				string temp = "";
				temp += current;
				warning(temp, 0);
				state = 0;
				current = get_next(in);
			}
			break;
		case 7:               //正在尝试识别指数标志e之后的去掉正负号的部分（尚未遇到有效数字，不可以结束）
			if (type == 2) //遇到了常数
			{
				token += current;
				state = 6;
				current = get_next(in);
			}
			else
				//错误原因：未识别到有效数字
				//恢复方法：默认视为识别了数字'0'
			{
				token += '0';
				string temp = "";
				temp += current;
				warning(temp, 0);
				state = 6;
				current = get_next(in);
			}
			break;
		case 8: //正在尝试识别运算符或界符
			cur_length++;
			if (token == "/") //注释开始标志
			{
				if (current == '*') //判定注释开始
				{
					state = 9;
					current = get_next(in);
					break;
				}
				else if (current == '/') //遇到了另一种形式的注释，直接寻找换行符
				{
					current = get_next(in);
					while (current != -1 && current != '\n')
						current = get_next(in);
					token = "";
					state = 0;
					break;
				}
			}
			else if (token == "\"") //字符串开始标志
			{
				state = 10;
				current = get_next(in);
				break;
			}
			else if (token == "\'") //字符开始标志
			{
				state = 11;
				current = get_next(in);
				break;
			}
			if (cur_length > 1 && type != 0)
				type = 2;
			switch (type)
			{
			case 0:
				state = 8;
				current = get_next(in);
				break;
			case 3: //类型：运算符，进入运算符识别模式
				token += current;
				state = 8;
				current = get_next(in);
				break;
			case 1:
			case 2:
			case 4: //遇到数字、标识符、界符均可认为识别已经结束
				cur_length = 0;
				if (op.find(token) != op.end())
					output(token, 2);
				else if (delimiter.find(token) != delimiter.end())
					output(token, 3);
				else
				{
					string temp1 = "";
					temp1 += token[0];
					string temp2 = "";
					temp2 += token[1];
					if (op.find(temp1) != op.end() && op.find(temp2) != op.end())
					{
						output(temp1, 2);
						output(temp2, 2);
					}
				}
				token = "";
				state = 0;
				break;
			default: //遇到非法字符 忽略
				string temp = "";
				temp += current;
				warning(temp, 0);
				state = 8;
				current = get_next(in);
				break;
			}
			break;
		case 9: //正在处理注释
			while (true)
			{
				current = get_next(in);
				if (current == -1)
					break;
				else if (current == '*')
				{
					current = get_next(in);
					if (current == -1) //文件结束
						break;
					else if (current == '/') //注释结束
					{
						current = get_next(in);
						state = 0;
						token = "";
						break;
					}
					else //默认补全另一半的注释符号
						continue;
				}
			}
			break;
		case 10: //正在读取字符串
			while (true)
			{
				current = get_next(in);
				if (current == -1)
					break;
				else if (current == '\"') //识别到了另一半引号
				{
					token += current;
					output(token, 5);
					token = "";
					state = 0;
					current = get_next(in);
					break;
				}
				else
					token += current;
			}
			break;
		case 11: //正在读取字符
			current = get_next(in);
			token += current;
			if (current == '\\')
			{
				current = get_next(in);
				token += current;
			}
			current = get_next(in);
			if (current == '\'')
			{
				token += '\'';
				output(token, 6);
				token = "";
				state = 0;
				current = get_next(in);
			}
			else
			{
				warning(token, 0);
				token += '\'';
				output(token, 6);
				token = "";
				state = 0;
			}
			break;
		default:
			exit(1);
		}
	}
}

char get_next(FILE* in)
{
	if (!feof(in)) {
		chars = chars + 1;
		return getc(in);
	}
	else
		return '\0';
}

void output(string data, int dt_type)
{
	static int lastlines = 1;
	if (lastlines < lines) {
		cout << endl << "<lines " << lines << ">:\t";
		lastlines = lines;
	}
	cout << "<" << data << "," << data_type[dt_type] << ">\t";
	each_type_nums[dt_type]++;
}

void warning(string data, int er_type)
{
	cout << "<Error:" << data << "," << error_type[er_type] << ">\t";
}

void print_counter()
{
	cout << endl;
	cout << "字符总数：" << chars << endl;
	cout << "行数：" << lines << endl;
	cout << "标识符数量：" << each_type_nums[0] << endl;
	cout << "常数数量：" << each_type_nums[1] << endl;
	cout << "运算符数量：" << each_type_nums[2] << endl;
	cout << "界符数量：" << each_type_nums[3] << endl;
	cout << "关键字数量：" << each_type_nums[4] << endl;
}